package fr.babos.graphic.components.containers.lists 
{
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import fr.babos.graphic.components.base.ResizableComponent;
	import fr.babos.graphic.components.containers.base.BaseContainer;
	import fr.babos.touch.delegate.IGestureTouchDelegate;
	import fr.babos.touch.emulator.GestureTouchEmulator;
	import fr.babos.touch.events.GestureTouchDragEvent;
	import fr.babos.touch.events.GestureTouchEvent;
	import org.osflash.signals.Signal;
	
	/**
	 * @author ZoulouX
	 */
	public class FreeVirtualList extends AVirtualList
	{
		/**
		 * L'index en début de liste
		 */
		protected var _firstIndex					:int;
		
		/**
		 * L'index en find de liste
		 */
		protected var _lastIndex					:int;
		
		/**
		 * L'inertie (1 infinie, 0 aucune)
		 */
		protected var _inertia						:Number						= .92;
		
		/**
		 * Le frein
		 */
		protected var _breakForce					:Number						= .2;
		
		
		/**
		 * L'index de début de liste
		 */
		public function get firstIndex ():uint { return _firstIndex; }
		
		/**
		 * L'index de fin de liste
		 */
		public function get lastIndex ():uint { return _lastIndex; }
		
		
		/**
		 * Le constructeur
		 * @param	pDelegate : Le delegate de cette liste qui va fournir les éléments et les informations sur le nombre d'éléments
		 * @param	pOrientation : L'orientation de la liste (voir statiques)
		 * @param	pBack : Si un fond transparent est généré automatiquement (permet d'intercepter tous les touchEvents)
		 */
		public function FreeVirtualList (pDelegate:IVirtualListDelegate, pOrientation:String = "vertical", pBack:Boolean = false)
		{
			// Relayer la construction
			super(pDelegate, pOrientation, pBack);
		}
		
		
		/**
		 * Initialisation du composant
		 */
		override protected function addedHandler (event:Event = null):void
		{
			// Relayer
			super.addedHandler(event);
			
			// Activer la boucle
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * La boucle pour les déplacements du contenu
		 */
		protected function enterFrameHandler (event:Event):void 
		{
			// Le déplacement n'est pas vérouillé par un drag
			if (!_dragLocked)
			{
				// Appliquer la vélocité
				if (_velocity != 0)
					currentScroll += _velocity;
				
				// Limiter les dépassements de la liste
				if (_needReplaceCheck)
					replaceList();
				
				// Si on a assez de vélocité
				if (_velocity <= -.5 || _velocity >= .5)
				{
					// Appliquer l'inertie
					_velocity *= _inertia;
				}
				
				// On annule le peu d'inertie qu'on a
				else if (_velocity != 0)
				{
					_velocity = 0;
				}
			}
		}
		
		/**
		 * Déplacement avec la molette
		 */
		override protected function mouseWheelHandler (event:MouseEvent):void
		{
			// Si la molette est autorisée
			if (_mouseWheelEnabled)
			{
				// On applique la vélocité
				_velocity -= event.delta * 4;
			}
		}
		
		/**
		 * Les évènements touch
		 * @param	event
		 */
		override public function gestureTouchHandler (event:GestureTouchEvent):void
		{
			// Si on est sur un event drag
			if (event is GestureTouchDragEvent)
			{
				// Récupérer l'event drag
				var dragEvent:GestureTouchDragEvent = event as GestureTouchDragEvent;
				
				// Un point vient vérouiller le déplacement (on a pas encore de direction)
				if (event.type == GestureTouchDragEvent.LOCK_DRAG)
				{
					// Si la liste est en mouvement
					if (_velocity != 0)
					{
						// Pas de vélocité
						_velocity = 0;
						
						// La liste a bougé
						_moved = TOUCH_THRESHOLD;
					}
					else
					{
						// La liste n'a pas bougé
						_moved = 0;
					}
					
					_touchPosition = new Point(stage.mouseX, stage.mouseY);
					
					// Le drag est vérouille
					_dragLocked = true;
				}
				
				// C'est dévérouillé
				else if (event.type == GestureTouchDragEvent.UNLOCK_DRAG)
				{
					// Le drag est dévérouillé
					_dragLocked = false;
				}
				
				// Vérifier si on est dans la bonne direction pour le drag
				else if (
						event.type == GestureTouchDragEvent.DRAGGING
						&&
						(
							dragEvent[_dragDirection]
							||
							(dragEvent.isBoth && _dragAllowUnknownDirection)
							||
							(dragEvent[_oppositeDragDirection] && _dragAllowOppositeDirection)
						)
					)
				{
					// La liste a bougé
					_moved += Math.sqrt(Math.pow(_touchPosition.x - stage.mouseX, 2) + Math.pow(_touchPosition.y - stage.mouseY, 2));
					_touchPosition.x = stage.mouseX;
					_touchPosition.y = stage.mouseY;
					
					// Récupérer la vélocité du point
					_velocity = dragEvent[_deltaTouchVar];
					
					// Récupérer la limite
					var limit:Number = getContentLimit();
					
					// Si on a une limite
					if (limit >= 0 || limit < 0)
					{
						// Diviser la vélocité
						_velocity = _velocity / _velocityOutBreak;
					}
					
					// On applique cette vélocité au scroll
					currentScroll += _velocity;
				}
			}
		}
		
		/**
		 * La liste dépasse, replacer la liste
		 * @param	pImmediate : Replacement immediat
		 */
		override protected function replaceList (pImmediate:Boolean = false):void
		{
			// Récupérer la limite
			var limit:Number = getContentLimit();
			
			// Si on a une limite
			if (limit >= 0 || limit < 0)
			{
				// Si le drag n'est pas vérouillé
				if (!_dragLocked)
				{
					// On freine le scroll vers cette limite
					_container[_offsetVar] -= limit * (pImmediate ? 1 : _breakForce);
				}
				
				// Diviser la vélocité
				_velocity = _velocity / _velocityOutBreak;
				
				// Si on est proche de la destination
				if (limit < .5  && limit > -.5 && _velocity < .5 && _velocity > -.5)
				{
					// On ne vérifie plus si on dépasse
					_needReplaceCheck = false;
					
					// On place à la destination
					_container[_offsetVar] -= limit;
				}
				
				// Signaler
				listMovedHandler();
			}
			else
			{
				// Pas de limite détéctée, plus besoin de tester
				_needReplaceCheck = false;
			}
		}
		
		override public function stop ():void
		{
			// Désactiver les listeners
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * Déstruction
		 */
		override public function dispose ():void
		{
			// Désactiver les listeners
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
			
			// Relayer
			super.dispose();
		}
	}
}